﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Collections.Concurrent;
using System.Threading;
namespace KStarProxy
{
    class SocketProcessor
    {
        public event EventHandler Connected;
        public event EventHandler Disconnected;

        private TcpClient _tcpClient;
        private DateTime _lastSendDatetime;

        private ConcurrentQueue<byte[]> _sendingDataQueue = new ConcurrentQueue<byte[]>();

        private AutoResetEvent _newDataEvent = new AutoResetEvent(false);
        private ManualResetEvent _stopEvent = new ManualResetEvent(false);
        private ManualResetEvent _sendThreadExitEvent = new ManualResetEvent(false);
        private Thread _sendThread;


        public void Start()
        {
            this._stopEvent.Reset();
            this._newDataEvent.Reset();
            this._sendThreadExitEvent.Reset();
            createSendThread();
        }
        public void Stop()
        {
            this._stopEvent.Set();
            this._sendThreadExitEvent.WaitOne();
            if (this._tcpClient != null)
                this._tcpClient.Close();
        }
        private void createSendThread()
        {
            ThreadStart ts = new ThreadStart(sendProcessor);
            _sendThread = new Thread(ts);
            _sendThread .Start ();
        }

        private void sendProcessor()
        {
            while (true)
            {
                if (this._stopEvent.WaitOne(0))
                    break;
                try
                {
                    if (this._sendingDataQueue.Any()
                        || this._newDataEvent.WaitOne())
                    {
                        var data = dequeue();
                        if (data != null && data.Any())
                            socketSend(data, data.Length);
                    }
                }
                catch (Exception ex)
                {
                
                }
            
            }
            _sendThreadExitEvent.Set();
        }

        private byte[] dequeue()
        { 
            byte[] data;
            if (this._sendingDataQueue.TryDequeue(out data))
            {
                return data;
            }
            else
                return null;
        }

        private bool socketSend(byte[] data, Int32 bufferSize)
        {
            lock (_tcpClient)
            {
                if (_tcpClient.Client == null || !_tcpClient.Client.Connected)
                    throw new SocketException();

                Int32 offset = 0;

                while (offset <data.Length)
                {
                    if (!_tcpClient.Client.Poll(1000, SelectMode.SelectWrite))
                    {
                        if (_tcpClient.Client == null || !_tcpClient.Client.Connected)
                            throw new SocketException();

                        continue;
                    }
                    int size = Math.Min(bufferSize, data.Length - offset);
                    Int32 nSend = _tcpClient.Client.Send(data, offset, size, SocketFlags.None);
                    offset += nSend;
                    _lastSendDatetime = DateTime.Now;
                }

                return true;
            }
        }


        public void SendData(byte[] data)
        {
            if (!IsConnected)
                return;
            _sendingDataQueue.Enqueue(data);
            this._newDataEvent.Set();
        }


        public bool IsConnected
        {
            get 
            {
                if (this._tcpClient != null && this._tcpClient.Client != null && this._tcpClient.Connected)
                    return true;
                else
                    return false;
            }
        }
    }
}
